#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Copyright (c) 2009 - 2025 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Script for eric to compile all .ui files to Python source.
"""

import os

from functools import partial

from PyQt6.uic import compileUi


def __pyName(py_dir, py_file):
    """
    Local function to create the Python source file name for the compiled
    .ui file.

    @param py_dir suggested name of the directory
    @type str
    @param py_file suggested name for the compile source file
    @type str
    @return tuple of directory name (string) and source file name
    @rtype str
    """
    return py_dir, "Ui_{0}".format(py_file)


def __compileOneUi(ui_path, mapFunc=None, execute=False, indent=4):
    """
    Function to compile a single form file to Python code.

    @param ui_path path of the Qt form file
    @type str
    @param mapFunc function to change directory and/or name of the resulting Python file
        (defaults to None)
    @type func (optional)
    @param execute flag indicating to generate code to execute the form in standalone
        mode (defaults to False)
    @type bool (optional)
    @param indent indentation width using spaces (defaults to 4)
    @type int (optional)
    """
    py_dir, py_file = os.path.split(ui_path[:-3] + ".py")

    # Allow the caller to change the name of the .py file or generate
    # it in a different directory.
    if mapFunc is not None:
        py_dir, py_file = mapFunc(py_dir, py_file)

    # Make sure the destination directory exists.
    os.makedirs(py_dir, exist_ok=True)

    py_path = os.path.join(py_dir, py_file)

    with open(py_path, "w", encoding="utf-8") as py_file:
        compileUi(ui_path, py_file, execute=execute, indent=indent)


def compileUiDir(root, recurse=False, mapFunc=None, workers=1, execute=False, indent=4):
    """
    Function to compile all Qt form files of a directory or directory tree
    to Python code.

    @param root directory to scan for Qt form files (i.e. files ending with '.ui'
    @type str
    @param recurse flag indicating to recurse into sub-directories (defaults to False)
    @type bool (optional)
    @param mapFunc function to change directory and/or name of the resulting Python file
        (defaults to None)
    @type func (optional)
    @param workers number of worker processes to be used to compile (defaults to 1)
    @type int (optional)
    @param execute flag indicating to generate code to execute the form in standalone
        mode (defaults to False)
    @type bool (optional)
    @param indent indentation width using spaces (defaults to 4)
    @type int (optional)
    """
    if recurse:
        ui_files = []
        for rootDir, _, files in os.walk(root):
            ui_files.extend(
                os.path.join(rootDir, ui) for ui in files if ui.endswith(".ui")
            )
    else:
        ui_files = [
            os.path.join(root, ui)
            for ui in os.listdir(root)
            if os.path.isfile(os.path.join(root, ui) and ui.endswith(".ui"))
        ]

    ProcessPoolExecutor = None
    if workers != 1:
        try:
            from concurrent.futures import ProcessPoolExecutor  # noqa: I-101, I-103
        except NotImplementedError:
            workers = 1

    if workers != 1 and ProcessPoolExecutor is not None:
        # If workers == 0, let ProcessPoolExecutor determine worker count.
        workers = workers or None
        with ProcessPoolExecutor(max_workers=workers) as executor:
            executor.map(
                partial(
                    __compileOneUi, mapFunc=mapFunc, execute=execute, indent=indent
                ),
                ui_files,
            )
    else:
        for ui_file in ui_files:
            __compileOneUi(ui_file, mapFunc=mapFunc, execute=execute, indent=indent)


def compileUiFiles():
    """
    Compile the .ui files to Python sources.
    """
    if os.path.exists("src"):
        # eric7 with 'src' layout
        compileUiDir(
            os.path.join("src", "eric7"), recurse=True, mapFunc=__pyName, workers=0
        )
    elif os.path.exists("eric7"):
        # old layout or invoked from within 'src'
        compileUiDir("eric7", recurse=True, mapFunc=__pyName, workers=0)
    else:
        print("No valid 'eric7' source layout could be found. Aborting...")


def main():
    """
    The main function of the script.
    """
    # Compile .ui files
    print("Compiling user interface files...")
    compileUiFiles()


if __name__ == "__main__":
    try:
        main()
    except SystemExit:
        raise
    except Exception:
        print(
            "\nAn internal error occured.  Please report all the output of the"
            " program, \nincluding the following traceback, to"
            " eric-bugs@eric-ide.python-projects.org.\n"
        )
        raise

#
# eflag: noqa = M-801
